// CowTippingScreen
#include "CowTippingScreen.h"

// Engine
#include "Engine.h"

// Physics
#include "HavokWrapper.h"

// for snprintf
#include <sstream>

//////////////////////////////////////////////////////////////////////////
// Helpers for CowTippingScene cameras
//////////////////////////////////////////////////////////////////////////
//------------------------------------------------------------------------
static const char* s_cowTippingCameraNames[] = {
	"CowTippingCameraDebug",			// DEBUG
	"CowTippingCameraFirstPerson",		// FIRST_PERSON
	"CowTippingCameraThirdPerson",		// THIRD_PERSON
	"CowTippingCameraRealTimeStrategy",	// REAL_TIME_STRATEGY
};

//------------------------------------------------------------------------
static const char* GetCameraName(GamePipeGame::CowTippingScreen::CameraType cameraType)
{
	return s_cowTippingCameraNames[cameraType];
}

//////////////////////////////////////////////////////////////////////////
// Helpers for CowTippingScene goblin animations
//////////////////////////////////////////////////////////////////////////
//------------------------------------------------------------------------
//------------------------------------------------------------------------
static const char* s_goblinAnimationNames[] = {
	"goblin_idle",		// IDLE
	"goblin_walk",		// WALK
	"goblin_attack",	// ATTACK
};

//------------------------------------------------------------------------
static const char* GetGoblinAnimationName(GamePipeGame::CowTippingScreen::GoblinState state)
{
	return s_goblinAnimationNames[state];
}

//////////////////////////////////////////////////////////////////////////
// CowTippingScreen constants
//////////////////////////////////////////////////////////////////////////
static const int MIN_SQUARED_TIPPING_DISTANCE = 2000;
static const int GRASS_FIELD_SIZE = 160;
static const Ogre::Real GRASS_HEIGHT = 12;
static const Ogre::Real GRASS_WIDTH = 40;
static const char* NIGHT_VISION_COMPOSITOR_NAME = "Night Vision";
static const char* GRASS_MESH_NAME = "grass";
static const char* GOBLIN_ENTITY_NAME = "goblin";
static const char* FARMHOUSE_ENTITY_NAME = "farmHouse";
static const Ogre::Vector3 GOBLIN_FORWARD_HEADING(0.0f, 0.0f, 30.0f);
static const Ogre::Radian LEFT_TURN_RADIANS = Ogre::Radian(Ogre::Math::DegreesToRadians(3.0f));
static const char* GOBLIN_THOUGHTS_EVENT_PATH = "CowTipping/music/goblin_thoughts";
static const char* GOBLIN_THOUGHTS_EVENT_NAME = "goblinThoughts";
static const char* COW_MOO_EVENT_PATH = "CowTipping/cows/moo";
static const char* COW_MOO_EVENT_NAME = "cowMoo";
static const char* s_gameCommandText[] = {
	"CONTROLLING THE COWS",
	"W->Move goblin or camera forward",
	"S->Move goblin or camera backward",
	"D->Turn goblin or camera right",
	"A->Turn goblin or camera left",
	"F->Tip cow",
	"V->Toggle camera view",
	"P->Play or pause video",
	"R->Restart video"
	"ESC->Quit"
};

namespace GamePipeGame
{
	//////////////////////////////////////////////////////////////////////////
	// Cow class definition
	//////////////////////////////////////////////////////////////////////////
	int CowTippingScreen::Cow::s_numCowsCreated = 0;
	//------------------------------------------------------------------------
	CowTippingScreen::Cow::Cow(Ogre::SceneManager* pSceneManager, Ogre::Vector3 position, Ogre::Degree rotationDegrees) :
		m_isTipping(false),
		m_isTipped(false),
		m_pitch(Ogre::Degree(0))
	{
		s_numCowsCreated++;

		std::stringstream nodeSS, entitySS;
		nodeSS << "CowNode" << s_numCowsCreated;
		entitySS << "CowEntity" << s_numCowsCreated;

		Ogre::Entity* cowEntity = pSceneManager->createEntity(entitySS.str(), "cow.mesh");
		m_pSceneNode = pSceneManager->getRootSceneNode()->createChildSceneNode(nodeSS.str(), position, Ogre::Quaternion(Ogre::Radian(rotationDegrees.valueRadians()), Ogre::Vector3::UNIT_Y));
		m_pSceneNode->scale(3.0f, 3.0f, 3.0f);
		m_pSceneNode->attachObject(cowEntity);
	}

	//------------------------------------------------------------------------
	void CowTippingScreen::Cow::Tip()
	{
		if (m_isTipped)
			return;

		m_isTipping = true;
		GamePipe::AudioManager::FMODEventPlay(COW_MOO_EVENT_NAME);
	}

	//------------------------------------------------------------------------
	void CowTippingScreen::Cow::Update(Ogre::Real timeSinceLastUpdate)
	{
		// if we're already tipped or we're not even tipping, there's nothing to update
		if (m_isTipped || !m_isTipping)
			return;

		Ogre::Degree incrementalPitch = timeSinceLastUpdate * (Ogre::Degree(15.0f) + m_pitch);
		m_pitch += incrementalPitch;
		m_pSceneNode->pitch(Ogre::Radian(incrementalPitch.valueRadians()));

		// are we done tipping yet?
		if (m_pitch >= Ogre::Degree(90))
			m_isTipped = true;
	}

	//////////////////////////////////////////////////////////////////////////
	// CowTippingScreen(std::string name)
	//////////////////////////////////////////////////////////////////////////
	CowTippingScreen::CowTippingScreen(std::string name) : GamePipe::GameScreen(name),
		m_currentActiveCamType(NONE), 
		m_pGoblinNode(0),
		m_pGoblinAnimState(0),
		m_GoblinState(IDLE),
		m_GoblinMoving(false),
		m_GoblinHeading(GOBLIN_FORWARD_HEADING),
		m_GoblinTurningState(NOT_TURNING),
		m_pGrassField(0),
		m_GoblinThoughtsSoundPlaying(false),
		m_pVideoTexture(0),
		m_VideoIsPlaying(false)
	{
		SetBackgroundUpdate(true);
		SetBackgroundDraw(true);
		SetIsPopup(false);
	}

	//////////////////////////////////////////////////////////////////////////
	// ~CowTippingScreen()
	//////////////////////////////////////////////////////////////////////////
	CowTippingScreen::~CowTippingScreen()
	{
	}

	//////////////////////////////////////////////////////////////////////////
	// Load()
	//////////////////////////////////////////////////////////////////////////
	bool CowTippingScreen::LoadResources()
	{
		GamePipe::ResourceManager::GetInstanceRef().LoadResourceGroup("General");
		GamePipe::ResourceManager::GetInstanceRef().LoadResourceGroup("CowTippingScreen");
		GamePipe::AudioManager::FMODInitializeAudio();
		GamePipe::AudioManager::FMODLoadAudio("CowTippingScreen");
		return true;
	}

	//////////////////////////////////////////////////////////////////////////
	// Unload()
	//////////////////////////////////////////////////////////////////////////
	bool CowTippingScreen::UnloadResources()
	{
		GamePipe::ResourceManager::GetInstanceRef().UnloadResourceGroup("General");
		GamePipe::ResourceManager::GetInstanceRef().UnloadResourceGroup("CowTippingScreen");
		GamePipe::AudioManager::FMODUnLoadAudio("CowTippingScreen");
		return true;
	}

	//////////////////////////////////////////////////////////////////////////
	// Initialize()
	//////////////////////////////////////////////////////////////////////////
	bool CowTippingScreen::Initialize()
	{
		SetBackgroundColor(Ogre::ColourValue(0.3921f, 0.5843f, 0.9294f, 1.0000f));
		m_fCameraMovementSpeed = 50.0;
		m_fCameraRotationSpeed = 5.0;
		Ogre::SceneManager* pSceneManager = GetDefaultSceneManager();

		//------------------------------------------------
		// Create goblin and set up skeletal animation
		//------------------------------------------------
		// create goblin
		Ogre::Entity* goblin = pSceneManager->createEntity(GOBLIN_ENTITY_NAME, "goblin.mesh");
		m_pGoblinNode = pSceneManager->getRootSceneNode()->createChildSceneNode("goblinNode", Ogre::Vector3(70,0,0));
		m_pGoblinNode->attachObject(goblin);
		m_pGoblinNode->scale(Ogre::Vector3(15, 15, 15));
		// set up goblin animation
		m_pGoblinAnimState = goblin->getAnimationState(GetGoblinAnimationName(m_GoblinState));
		m_pGoblinAnimState->setLoop(true);
		m_pGoblinAnimState->setEnabled(true);

		//------------------------------------------------
		// Create all of our cows
		//------------------------------------------------
		CreateCows();

		//------------------------------------------------
		// Load cube with video
		//------------------------------------------------
		// Initialize Video on Cube
		pSceneManager->getRootSceneNode()->createChildSceneNode()->attachObject(pSceneManager->createEntity("Cube", "cube.mesh"));
		pSceneManager->getEntity("Cube")->getParentSceneNode()->setPosition(250, 50, 280);
		pSceneManager->getEntity("Cube")->getParentSceneNode()->scale(Ogre::Vector3(1, 1, 1));
		pSceneManager->getEntity("Cube")->setMaterialName("VideoTexture");

		// Set Video in Pause mode to start with
		m_pVideoTexture = (Ogre::CWMVideoTexture*)Ogre::ExternalTextureSourceManager::getSingleton().getExternalTextureSource("wmvideo");
		m_pVideoTexture->setPlayMode(Ogre::TextureEffectPlay_Looping);
		m_VideoIsPlaying = false;

		//------------------------------------------------
		// Create all cameras
		//------------------------------------------------
		// Debug camera
		m_cameras[DEBUG].pCamera = GetActiveCamera();
		m_cameras[DEBUG].pCamera->setPosition(0, 50, 100);

		// Create real-time strategy camera (passing in NULL as 3rd param adds camera to the scene)
		m_cameras[REAL_TIME_STRATEGY].pCamera = pSceneManager->createCamera(GetCameraName(REAL_TIME_STRATEGY));
		m_cameras[REAL_TIME_STRATEGY].pCamera->setPosition(Ogre::Vector3(0, 375, -600));
		m_cameras[REAL_TIME_STRATEGY].pCamera->lookAt(Ogre::Vector3::ZERO);
		m_cameras[REAL_TIME_STRATEGY].pCamera->setFarClipDistance(1000.0f);
		// the RTS camera will appear at the bottom right hand side of the window;
		// its width and height should be 1/4 of the total window size
		m_cameras[REAL_TIME_STRATEGY].viewportLeft = m_cameras[REAL_TIME_STRATEGY].viewportTop = 0.75f;
		m_cameras[REAL_TIME_STRATEGY].viewportWidth = m_cameras[REAL_TIME_STRATEGY].viewportHeight = 0.25f;

		// Create third-person camera
		// Can't use a GameCamera because the goblin entity is already attached to the goblin node.
		// Attaching the goblin entity to a TPS game camera isn't an option either since you can't
		// scale the size of a camera, and the goblin entity needs to be scaled.	
		m_cameras[THIRD_PERSON].pCamera = pSceneManager->createCamera(GetCameraName(THIRD_PERSON));
		m_cameras[THIRD_PERSON].pCamera->setPosition(Ogre::Vector3(0, 50, -130));
		m_cameras[THIRD_PERSON].pCamera->lookAt(Ogre::Vector3(0, 15, 0));
		m_cameras[THIRD_PERSON].pCamera->setNearClipDistance(5);
		m_pGoblinNode->attachObject(m_cameras[THIRD_PERSON].pCamera);

		// Create first-person camera
		Ogre::Vector3 fpsCameraPos(0.0f, 27.0f, 2.0f);
		m_cameras[FIRST_PERSON].pCamera = pSceneManager->createCamera(GetCameraName(FIRST_PERSON));
		m_cameras[FIRST_PERSON].pCamera->setPosition(fpsCameraPos);
		m_cameras[FIRST_PERSON].pCamera->lookAt(fpsCameraPos + Ogre::Vector3(0, 0, 1.0f));
		m_cameras[FIRST_PERSON].pCamera->setNearClipDistance(12);
		m_pGoblinNode->attachObject(m_cameras[FIRST_PERSON].pCamera);

		//------------------------------------------------
		// Set up lighting and sky dome
		//------------------------------------------------
		pSceneManager->setAmbientLight(Ogre::ColourValue(0.5000, 0.5000, 0.5000, 0.5000));
		pSceneManager->setSkyDome(true, "CowTipping/CloudySky");

		//------------------------------------------------
		// Load our scene and create the grass mesh
		//------------------------------------------------
		LoadScene("CowTippingScreen.scene");
		SetUpGrass();

		//------------------------------------------------
		// Add smoke particle system to farm house chimney
		//------------------------------------------------
		// farm house & farm house node were added by loading the .scene file
		Ogre::SceneNode* farmHouseNode = (Ogre::SceneNode*)pSceneManager->getEntity(FARMHOUSE_ENTITY_NAME)->getParentNode();
		farmHouseNode->attachObject(pSceneManager->createParticleSystem("Smoke", "SmokeParticleSystem"));

		//------------------------------------------------
		// Set up sounds
		//------------------------------------------------
		GamePipe::AudioManager::FMODSetEventSound(GOBLIN_THOUGHTS_EVENT_PATH, GOBLIN_THOUGHTS_EVENT_NAME);
		GamePipe::AudioManager::FMODSetEventSound(COW_MOO_EVENT_PATH, COW_MOO_EVENT_NAME);

		//------------------------------------------------
		// Add debug text
		//------------------------------------------------
		std::string commandText;
		for (int idx = 0; idx < sizeof(s_gameCommandText)/sizeof(s_gameCommandText[0]); idx++)
		{
			commandText += s_gameCommandText[idx];
			commandText += "\n";
		}
		UtilitiesPtr->Add("CowTippingDemoCommands", new GamePipe::DebugText(commandText.c_str(), (Ogre::Real)0.005, (Ogre::Real)0.005, (Ogre::Real)2.5, Ogre::ColourValue(1.0f, 1.0f, 1.0f, 0.75f), Ogre::TextAreaOverlayElement::Left));
	
		//TODO-SV: delete GameObject when an Entity is deleted in GLE cant be done yet
		return true;
	}

	//////////////////////////////////////////////////////////////////////////
	// Destroy()
	//////////////////////////////////////////////////////////////////////////
	bool CowTippingScreen::Destroy()
	{
		Ogre::MeshManager::getSingleton().remove(GRASS_MESH_NAME);
		return true;
	}

	//////////////////////////////////////////////////////////////////////////
	// Show()
	//////////////////////////////////////////////////////////////////////////
	bool CowTippingScreen::Show()
	{
		// we need to show ALL cameras once before our RTS camera 
		// so that the RTS camera always appears in front.

		//------------------------------------------------
		// Show debug camera, initially NOT active
		//------------------------------------------------
		ShowCamera(DEBUG, false);

		//------------------------------------------------
		// Show first-person camera, intially NOT active
		//------------------------------------------------
		ShowCamera(FIRST_PERSON, false, NIGHT_VISION_COMPOSITOR_NAME);

		//------------------------------------------------
		// Show third-person as active camera by default
		//------------------------------------------------
		// NOTE: we show this camera after the debug and first person cameras
		// because we want to default to using the 3rd person camera. When
		// a viewport is created (i.e. the first time we call ShowCamera with
		// that camera type), it will automatically appear on the screen.
		ShowCamera(THIRD_PERSON);

		//------------------------------------------------
		// Always show RTS viewport (mini-map style)
		//------------------------------------------------
		// don't ever make the RTS camera the active camera!
		ShowCamera(REAL_TIME_STRATEGY, false);

		return true;
	}

	//////////////////////////////////////////////////////////////////////////
	// Update()
	//////////////////////////////////////////////////////////////////////////
	bool CowTippingScreen::Update()
	{
		Ogre::Real timeSinceLastUpdate = Ogre::Real(EnginePtr->GetDeltaTime());

		// Update our goblin
		UpdateGoblin(timeSinceLastUpdate);

		// Update all our cows
		for (int idx = 0; idx < NUM_COWS; idx++)
			m_cows[idx]->Update(timeSinceLastUpdate);

		// Keep the grass waving :)
		WaveGrass(timeSinceLastUpdate);

		// Update default camera if we're using it
		if (m_currentActiveCamType == DEBUG)
			FreeCameraMovement();

		// Background music update
		UpdateMusic();

		// Update for 3D sounds
		GamePipe::AudioManager::FMODUpdateListener(m_pGoblinNode->getPosition(), m_pGoblinNode->getOrientation());     // update 'ears'
		GamePipe::AudioManager::FMODeventSystem->update();   // needed to update 3d engine, once per frame.

		return true;
	}

	//////////////////////////////////////////////////////////////////////////
	// Draw()
	//////////////////////////////////////////////////////////////////////////
	bool CowTippingScreen::Draw()
	{
		return true;
	}

	//////////////////////////////////////////////////////////////////////////
	// OnKeyPress()
	//////////////////////////////////////////////////////////////////////////
	bool CowTippingScreen::OnKeyPress(const GIS::KeyEvent& keyEvent)
	{
		// NOTE: We do NOT handle goblin keyboard input in the switch statement
		// because the user may be holding down multiple gobling input keys and
		// we should handle all of them!
		switch (keyEvent.key)
		{
		case GIS::KC_ESCAPE: // quit our demo
			{
				Close();
				break;
			}

		case GIS::KC_V: // toggle the viewport
			{
				if (m_currentActiveCamType == DEBUG) // currently using debug camera
				{
					// after DEBUG camera, show the third person camera
					ShowCamera(THIRD_PERSON);
				}				
				else if (m_currentActiveCamType == THIRD_PERSON) // currently using third person camera
				{
					// after the third person camera, show the first person camera
					ShowCamera(FIRST_PERSON); 
				}
				else // currently using first person camera
				{
					// after the first person camera, go back to the debug camera
					ShowCamera(DEBUG);
				}
				break;
			}

		case GIS::KC_P: // play or pause video
			{
				if (m_VideoIsPlaying)
				{
					m_VideoIsPlaying = !m_VideoIsPlaying;
					m_pVideoTexture->pause("VideoTexture");
				}
				else
				{
					m_VideoIsPlaying = !m_VideoIsPlaying;
					m_pVideoTexture->start("VideoTexture");
				}
				break;
			}

		case GIS::KC_R: // restart video
			{
				m_pVideoTexture->restart("VideoTexture");
				m_pVideoTexture->start("VideoTexture");
				m_VideoIsPlaying = true;
				break;
			}
		}

		// if we're currently using the debug camera, don't allow input for the goblin
		if (m_currentActiveCamType != DEBUG)
			GoblinOnKeyPress(keyEvent);

		return true;
	}

	//////////////////////////////////////////////////////////////////////////
	// OnKeyRelease()
	//////////////////////////////////////////////////////////////////////////
	bool CowTippingScreen::OnKeyRelease(const GIS::KeyEvent& keyEvent)
	{
		// if we're currently using the debug camera, don't allow input for the goblin
		if (m_currentActiveCamType != DEBUG)
			GoblinOnKeyRelease(keyEvent);

		return true;
	}

	//////////////////////////////////////////////////////////////////////////
	// GoblinOnKeyPress(const GIS::KeyEvent &keyEvent)
	//////////////////////////////////////////////////////////////////////////
	void CowTippingScreen::GoblinOnKeyPress(const GIS::KeyEvent &keyEvent)
	{
		//------------------------------------------------
		// Is our goblin moving?
		//------------------------------------------------
		// move goblin forward
		if (keyEvent.key == GIS::KC_W) 
		{
			m_GoblinMoving = true;
			m_GoblinHeading = GOBLIN_FORWARD_HEADING;
			SwitchGoblinAnim(WALK);
		}
		// move goblin backward
		else if (keyEvent.key == GIS::KC_S)
		{
			m_GoblinMoving = true;
			m_GoblinHeading = -GOBLIN_FORWARD_HEADING;
			SwitchGoblinAnim(WALK);
		}

		//------------------------------------------------
		// Is our goblin turning?
		//------------------------------------------------
		// turn goblin right
		if (keyEvent.key == GIS::KC_D)
			m_GoblinTurningState = TURNING_RIGHT;
		// turn goblin left
		else if (keyEvent.key == GIS::KC_A)
			m_GoblinTurningState = TURNING_LEFT;

		//------------------------------------------------
		// Is our goblin attacking?
		//------------------------------------------------
		// goblin attack!
		if (keyEvent.key == GIS::KC_F)
		{
			SwitchGoblinAnim(ATTACK, false);
			DoCowTipping();
		}
	}

	//////////////////////////////////////////////////////////////////////////
	// GoblinOnKeyRelease(const GIS::KeyEvent &keyEvent)
	//////////////////////////////////////////////////////////////////////////
	void CowTippingScreen::GoblinOnKeyRelease(const GIS::KeyEvent &keyEvent)
	{
		switch (keyEvent.key)
		{
		// releasing either of the movement keys should make our goblin stop moving
		case GIS::KC_W:
		case GIS::KC_S:
			m_GoblinMoving = false;
			SwitchGoblinAnim(IDLE);
			break;
		// releasing either of the turning keys should make our goblin stop turning
		case GIS::KC_D:
		case GIS::KC_A:
			m_GoblinTurningState = NOT_TURNING;
			break;
		}
	}

	//////////////////////////////////////////////////////////////////////////
	// DoCowTipping()
	//////////////////////////////////////////////////////////////////////////
	void CowTippingScreen::DoCowTipping()
	{
		Ogre::Vector3 goblinPos = m_pGoblinNode->getPosition();
		for (int idx = 0; idx < NUM_COWS; idx++)
		{
			// if we can't tip this cow, skip it
			if (!m_cows[idx]->CanBeTipped())
				continue;

			// are we close enough to tip the cow?
			Ogre::Real squaredDistToCow = goblinPos.squaredDistance(m_cows[idx]->GetPosition());
			if (squaredDistToCow <= MIN_SQUARED_TIPPING_DISTANCE)
				m_cows[idx]->Tip();
		}
	}

	//////////////////////////////////////////////////////////////////////////
	// UpdateMusic()
	//////////////////////////////////////////////////////////////////////////
	void CowTippingScreen::UpdateMusic()
	{
		if (m_currentActiveCamType == FIRST_PERSON)
		{
			if (!m_GoblinThoughtsSoundPlaying)
			{
				GamePipe::AudioManager::FMODEventPlay(GOBLIN_THOUGHTS_EVENT_NAME);
				m_GoblinThoughtsSoundPlaying = true;
			}
		}
		else
		{
			if (m_GoblinThoughtsSoundPlaying)
			{
				GamePipe::AudioManager::FMODEventPaused(GOBLIN_THOUGHTS_EVENT_NAME);
				m_GoblinThoughtsSoundPlaying = false;
			}
		}
	}

	//////////////////////////////////////////////////////////////////////////
	// ShowCamera(CameraType type)
	//////////////////////////////////////////////////////////////////////////
	void CowTippingScreen::ShowCamera(CameraType type, bool setAsActiveCam /*= true*/, const char* compositorName /*= 0*/)
	{
		// if this is already our currently active camera type, there's nothing to do
		if (m_currentActiveCamType == type)
			return;
		
		// make sure the camera exists!
		assert(m_cameras[type].pCamera);

		// if this camera's viewport index is -1, we haven't set a viewport for it!
		if (m_cameras[type].viewportIndex == -1)
		{
			// get the viewport index for this camera
			m_cameras[type].viewportIndex = EnginePtr->GetFreeViewportIndex();

			// set up this camera's viewport
			Ogre::Viewport* pViewport = EnginePtr->GetRenderWindow()->addViewport(m_cameras[type].pCamera, m_cameras[type].viewportIndex, m_cameras[type].viewportLeft, m_cameras[type].viewportTop, m_cameras[type].viewportWidth, m_cameras[type].viewportHeight);
			pViewport->setBackgroundColour(GetBackgroundColor());
			pViewport->setOverlaysEnabled(true);	
			m_cameras[type].pCamera->setAspectRatio(static_cast<Ogre::Real>(pViewport->getActualWidth()) / static_cast<Ogre::Real>(pViewport->getActualHeight()));

			// if we are using a compositor, register that compositor here and enable it
			if (compositorName != 0)
			{
				Ogre::CompositorInstance* pCompositor = Ogre::CompositorManager::getSingleton().addCompositor(pViewport, compositorName);
				Ogre::CompositorManager::getSingleton().setCompositorEnabled(pViewport, compositorName, true);
			}
		}

		if (setAsActiveCam)
		{
			if (m_currentActiveCamType != NONE)
			{
				Ogre::Viewport* oldViewport = EnginePtr->GetRenderWindow()->getViewport(m_cameras[m_currentActiveCamType].viewportIndex);
				// move the previous camera's viewport out of the way
				oldViewport->setDimensions(1.0f, 1.0f, m_cameras[m_currentActiveCamType].viewportWidth, m_cameras[m_currentActiveCamType].viewportHeight);
			}

			Ogre::Viewport* newViewport = EnginePtr->GetRenderWindow()->getViewport(m_cameras[type].viewportIndex);
			// move the new camera's viewport into its proper place
			newViewport->setDimensions(m_cameras[type].viewportLeft, m_cameras[type].viewportTop, m_cameras[type].viewportWidth, m_cameras[type].viewportHeight);
			
			m_currentActiveCamType = type;
		}
	}

	//////////////////////////////////////////////////////////////////////////
	// UpdateGoblin()
	//////////////////////////////////////////////////////////////////////////
	void CowTippingScreen::UpdateGoblin(Ogre::Real timeSinceLastUpdate)
	{
		// if we're currently attacking and the animation is over, switch back to idling
		if (m_GoblinState == ATTACK && m_pGoblinAnimState->getTimePosition() >= m_pGoblinAnimState->getLength())
			SwitchGoblinAnim(IDLE);

		// Update direction the goblin is heading
		switch (m_GoblinTurningState)
		{
		case TURNING_LEFT:
			m_pGoblinNode->yaw(LEFT_TURN_RADIANS);
			break;
		case TURNING_RIGHT:
			m_pGoblinNode->yaw(-LEFT_TURN_RADIANS);
			break;
		}

		// Update goblin position
		if (m_GoblinMoving)
			m_pGoblinNode->translate(m_pGoblinNode->getOrientation() * m_GoblinHeading * timeSinceLastUpdate);

		// Update goblin animation
		m_pGoblinAnimState->addTime(timeSinceLastUpdate);
	}

	//////////////////////////////////////////////////////////////////////////
	// SwitchGoblinAnim(GoblinState state)
	//////////////////////////////////////////////////////////////////////////
	void CowTippingScreen::SwitchGoblinAnim(GoblinState state, bool loopAnimation /*= true*/)
	{
		// nothing to do if we're already in this state
		if (m_GoblinState == state)
			return;

		// we're in a new state now
		m_GoblinState = state;

		// reset the clock on the current animation so it'll start from the
		// beginning the next time that we load it
		m_pGoblinAnimState->setTimePosition(0);

		// now load the new animation state
		m_pGoblinAnimState = GetDefaultSceneManager()->getEntity(GOBLIN_ENTITY_NAME)->getAnimationState(GetGoblinAnimationName(m_GoblinState));
		m_pGoblinAnimState->setEnabled(true);
		m_pGoblinAnimState->setLoop(loopAnimation);
	}

	//////////////////////////////////////////////////////////////////////////
	// CreateCows()
	//////////////////////////////////////////////////////////////////////////
	void CowTippingScreen::CreateCows()
	{
		for (int idx = 0; idx < NUM_COWS; idx++)
		{
			Ogre::Real maxPos = GRASS_FIELD_SIZE * 1.5f;
			Ogre::Real minPos = -maxPos;
			
			Ogre::Real x = Ogre::Math::RangeRandom(minPos, maxPos);
			Ogre::Real z = Ogre::Math::RangeRandom(minPos, maxPos);
			m_cows[idx] = new Cow(GetDefaultSceneManager(), Ogre::Vector3(x, 0, z), Ogre::Degree(Ogre::Math::RangeRandom(0.0f, 359.0f)));
		}
	}

	//////////////////////////////////////////////////////////////////////////
	// SetUpGrass()
	// Taken from Ogre grass sample.
	//////////////////////////////////////////////////////////////////////////
	void CowTippingScreen::SetUpGrass()
	{
		Ogre::SceneManager* pSceneMgr = GetDefaultSceneManager();

		// create our grass mesh, and create a grass entity from it
		CreateGrassMesh();
		Ogre::Entity* grass = pSceneMgr->createEntity("Grass", GRASS_MESH_NAME);
		
		// create a static geometry field, which we will populate with grass
		m_pGrassField = pSceneMgr->createStaticGeometry("GrassField");
		Ogre::Vector3 regionDimensions = Ogre::Vector3(Ogre::Real(GRASS_FIELD_SIZE), Ogre::Real(GRASS_FIELD_SIZE), Ogre::Real(GRASS_FIELD_SIZE));
		Ogre::Vector3 origin = -regionDimensions / 2;
		m_pGrassField->setRegionDimensions(regionDimensions);
		m_pGrassField->setOrigin(origin);

		// add grass uniformly throughout the field, with some random variations
		for (int x = -GRASS_FIELD_SIZE * 2; x < GRASS_FIELD_SIZE * 2; x += 10)
		{
			for (int z = -GRASS_FIELD_SIZE * 2; z < GRASS_FIELD_SIZE * 2; z += 10)
			{
				Ogre::Vector3 pos(x + Ogre::Math::RangeRandom(-4, 4), 0, z + Ogre::Math::RangeRandom(-4, 4));
				Ogre::Quaternion ori(Ogre::Degree(Ogre::Math::RangeRandom(0, 359)), Ogre::Vector3::UNIT_Y);
				Ogre::Vector3 scale(1.0f, Ogre::Math::RangeRandom(0.85f, 1.15f), 1.0f);

				m_pGrassField->addEntity(grass, pos, ori, scale);
			}
		}

		m_pGrassField->build();  // build our static geometry (bake the grass into it)
	}

	//////////////////////////////////////////////////////////////////////////
	// CreateGrassMesh()
	// Taken from Ogre grass sample.
	//////////////////////////////////////////////////////////////////////////
	void CowTippingScreen::CreateGrassMesh()
	{
		Ogre::MeshPtr mesh = Ogre::MeshManager::getSingleton().createManual(GRASS_MESH_NAME, Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);

		// create a submesh with the grass material
		Ogre::SubMesh* sm = mesh->createSubMesh();
		sm->setMaterialName("CowTipping/GrassBlades");
		sm->useSharedVertices = false;
		sm->vertexData = OGRE_NEW Ogre::VertexData();
		sm->vertexData->vertexStart = 0;
		sm->vertexData->vertexCount = 12;
		sm->indexData->indexCount = 18;

		// specify a vertex format declaration for our mesh: 3 floats for position, 3 floats for normal, 2 floats for UV
		Ogre::VertexDeclaration* decl = sm->vertexData->vertexDeclaration;
		decl->addElement(0, 0, Ogre::VET_FLOAT3, Ogre::VES_POSITION);
        decl->addElement(0, sizeof(float) * 3, Ogre::VET_FLOAT3, Ogre::VES_NORMAL);
        decl->addElement(0, sizeof(float) * 6, Ogre::VET_FLOAT2, Ogre::VES_TEXTURE_COORDINATES, 0);

		// create a vertex buffer
		Ogre::HardwareVertexBufferSharedPtr vb = Ogre::HardwareBufferManager::getSingleton().createVertexBuffer
			(decl->getVertexSize(0), sm->vertexData->vertexCount, Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY);

		GrassVertex* verts = (GrassVertex*)vb->lock(Ogre::HardwareBuffer::HBL_DISCARD);  // start filling in vertex data

		for (unsigned int i = 0; i < 3; i++)  // each grass mesh consists of 3 planes
		{
			// planes intersect along the Y axis with 60 degrees between them
			Ogre::Real x = Ogre::Math::Cos(Ogre::Degree(Ogre::Real(i * 60))) * GRASS_WIDTH / Ogre::Real(2);
			Ogre::Real z = Ogre::Math::Sin(Ogre::Degree(Ogre::Real(i * 60))) * GRASS_WIDTH / Ogre::Real(2);

			for (unsigned int j = 0; j < 4; j++)  // each plane has 4 vertices
			{
				GrassVertex& vert = verts[i * 4 + j];

				vert.x = j < 2 ? -x : x;
				vert.y = j % 2 ? 0 : GRASS_HEIGHT;
				vert.z = j < 2 ? -z : z;

				// all normals point straight up
				vert.nx = 0;
				vert.ny = 1;
				vert.nz = 0;

				vert.u = j < 2 ? 0.0f : 1.0f;
				vert.v = (float)(j % 2);
			}
		}

		vb->unlock();  // commit vertex changes

		sm->vertexData->vertexBufferBinding->setBinding(0, vb);  // bind vertex buffer to our submesh

		// create an index buffer
		sm->indexData->indexBuffer = Ogre::HardwareBufferManager::getSingleton().createIndexBuffer
			(Ogre::HardwareIndexBuffer::IT_16BIT, sm->indexData->indexCount, Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY);

		// start filling in index data
		Ogre::uint16* indices = (Ogre::uint16*)sm->indexData->indexBuffer->lock(Ogre::HardwareBuffer::HBL_DISCARD);

		for (unsigned int i = 0; i < 3; i++)  // each grass mesh consists of 3 planes
		{
			unsigned int off = i * 4;  // each plane consists of 2 triangles

			*indices++ = 0 + off;
			*indices++ = 3 + off;
			*indices++ = 1 + off;

			*indices++ = 0 + off;
			*indices++ = 2 + off;
			*indices++ = 3 + off;
		}

		sm->indexData->indexBuffer->unlock();  // commit index changes
	}

	//////////////////////////////////////////////////////////////////////////
	// WaveGrass()
	// Taken from Ogre grass sample.
	//////////////////////////////////////////////////////////////////////////
	void CowTippingScreen::WaveGrass(Ogre::Real timeElapsed)
	{
		if (!m_pGrassField)
			return;

		static Ogre::Real xinc = Ogre::Math::PI * Ogre::Real(0.3);
		static Ogre::Real zinc = Ogre::Math::PI * Ogre::Real(0.44);
		static Ogre::Real xpos = Ogre::Math::RangeRandom(-Ogre::Math::PI, Ogre::Math::PI);
		static Ogre::Real zpos = Ogre::Math::RangeRandom(-Ogre::Math::PI, Ogre::Math::PI);
		static Ogre::Vector4 offset(0, 0, 0, 0);

		xpos += xinc * timeElapsed;
		zpos += zinc * timeElapsed;

		// update vertex program parameters by binding a value to each renderable
		Ogre::StaticGeometry::RegionIterator regs =  m_pGrassField->getRegionIterator();
		while (regs.hasMoreElements())
		{
			Ogre::StaticGeometry::Region* reg = regs.getNext();

			// a little randomness
			xpos += reg->getCentre().x * Ogre::Real(0.001);
			zpos += reg->getCentre().z * Ogre::Real(0.001);
			offset.x = Ogre::Math::Sin(xpos) * 4;
			offset.z = Ogre::Math::Sin(zpos) * 4;

			Ogre::StaticGeometry::Region::LODIterator lods = reg->getLODIterator();
			while (lods.hasMoreElements())
			{
				Ogre::StaticGeometry::LODBucket::MaterialIterator mats = lods.getNext()->getMaterialIterator();
				while (mats.hasMoreElements())
				{
					Ogre::StaticGeometry::MaterialBucket::GeometryIterator geoms = mats.getNext()->getGeometryIterator();
					while (geoms.hasMoreElements()) geoms.getNext()->setCustomParameter(999, offset);
				}
			}
		}
	}
}